package com.lumlord.common.shiro.service;

import java.util.Date;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.lumlord.common.domain.User;
import com.lumlord.common.exception.BusinessException;
import com.lumlord.common.shiro.util.JWTToken;
import com.lumlord.common.shiro.util.ShiroUtils;
import com.lumlord.model.auto.TsysUser;
import com.lumlord.service.CommonService;
import com.lumlord.service.SystemUserService;
import com.lumlord.util.MD5Util;

/**
 * 身份校验核心类
 * 
 * @ClassName: MyShiroRealm
 * @author gpf
 * @date 2019-9-10
 *
 */
@Component
public class DbShiroRealm extends AuthorizingRealm {
	public static final String TOOKEN = "token";

	DbShiroRealm() {
		this.setCredentialsMatcher(new JWTCredentialsMatcher());
	}

	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof UsernamePasswordToken || token instanceof PhoneToken || token instanceof JWTToken;
	}
	
	public boolean userValidate(User userInfo) throws BusinessException {
		if (userInfo == null){
			throw new AuthenticationException("用户不存在!");
		}else if (1 == userInfo.getState()) {
			throw new AuthenticationException("用户已被删除，请联系管理员!");
		}else if (1 == userInfo.getStatus()) {
			throw new AuthenticationException("账号被禁用，请联系管理员!");
		} else if(null!=userInfo.getQuitDate()&&new Date().after(userInfo.getQuitDate())){
			throw new AuthenticationException("您已离职，请联系管理员!");
		}else if(systemUserService.postValid(userInfo.getPostId())){
			throw new AuthenticationException("您的岗位已设置无效，请联系管理员!");
		}else{
			return false;
		}
	}

	public SimpleAuthenticationInfo userLogin(User userInfo) throws BusinessException {
		if (userValidate(userInfo)){
			 return null;
		}else {
			try {
				systemUserService.putInfo(userInfo);
			} catch (BusinessException e) {
				throw new AuthenticationException(e.getMessage());
			}

			SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, // 用户名
					userInfo.getPassword(), // 密码
					getName() // realm name
			);

			try {
				systemUserService.writeLoginInfo(userInfo);
				ShiroUtils.setUser(userInfo);
			} catch (BusinessException e) {
				throw new AuthenticationException("tooken写入失败，请重新登录！");
			}

			return authenticationInfo;

		}

	}

	 

	/** 权限 */
	@SuppressWarnings("unused")
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 加这一步的目的是在Post请求的时候会先进认证，然后在到请求
		if (token.getPrincipal() == null) {
			return null;
		}

		if (token instanceof PhoneToken) {
			PhoneToken phoneToken = (com.lumlord.common.shiro.service.PhoneToken) token;
			User userInfo;
			try {
				userInfo = systemUserService.selectUserByMobilePhone(phoneToken.getPhone());
				return userLogin(userInfo);
			} catch (BusinessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		} else { // 用户名密码登录
			String username = (String) token.getPrincipal();
			// String password = new String((char[]) token.getCredentials());
			// 通过username从数据库中查找 User对象，如果找到，没找到.
			// 实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
			User userInfo = systemUserService.selectUserByMobileAccount(username);
			try {
				return userLogin(userInfo);
			} catch (BusinessException e) {
				e.printStackTrace();
			}
		}
			return null;
	}

	/**
	 * 认证 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");
		if (principals == null) {
			throw new AuthorizationException("principals should not be null");
		}

		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		TsysUser userinfo = (TsysUser) principals.getPrimaryPrincipal();
		Integer uid = userinfo.getId();

		/*
		 * List<TsysRole> tsysRoles = roleDao.queryUserRole(uid);
		 * 
		 * for (TsysRole userrole : tsysRoles) { //
		 * System.out.println("角色名字:"+gson.toJson(userrole)); String rolid =
		 * userrole.getId();// 角色id authorizationInfo.addRole(userrole.getName());//
		 * 添加角色名字 List<TsysPremission> premissions = permissionDao.queryRoleId(rolid);
		 * for (TsysPremission p : premissions) { //
		 * System.out.println("角色下面的权限:"+gson.toJson(p)); if
		 * (StringUtils.isNotEmpty(p.getPerms())) {
		 * authorizationInfo.addStringPermission(p.getPerms()); }
		 * 
		 * } } Session session = SecurityUtils.getSubject().getSession();
		 */

		return authorizationInfo;
	}

	/**
	 * 清理缓存权限
	 */
	public void clearCachedAuthorizationInfo() {
		this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
	}
	
	
	@PostConstruct
	public void initCredentialsMatcher() { // 自定义密码 验证
		//该句作用是重写shiro的密码验证，让shiro用我自己的验证  
		setCredentialsMatcher(new SimpleCredentialsMatcher() {
			@Override
			public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
				if (authcToken instanceof PhoneToken) { // 手机认证 免去密码 认证
					return true;
				}
				UsernamePasswordToken token = (UsernamePasswordToken) authcToken;

				Object tokenCredentials = encrypt(String.valueOf(token.getPassword()));
				Object accountCredentials = getCredentials(info);
				// 将密码加密与系统加密后的密码校验，内容一致就返回true,不一致就返回false
				return equals(tokenCredentials, accountCredentials);
			}
			// 将传进来密码加密方法
			private String encrypt(String data) {
				return MD5Util.encode(data);
			}
		});
	}
	
	@Autowired 
	private HttpServletRequest httpServletRequest;
	
	@Autowired 
	private CommonService commonService;
	@Autowired
	private SystemUserService systemUserService;
}
